package com.axzhengxin.platform.axcube.service.impl;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.axzhengxin.platform.axcube.service.IElasticSearchAgentService;
import com.axzhengxin.platform.axcube.transf.ElasticSearchAgentRequest;
import com.axzhengxin.platform.axcube.transf.ElasticSearchAgentResponse;
import com.axzhengxin.platform.axcube.transf.EntInfo;
import com.axzhengxin.platform.common.util.ElasticSearchUtil;
import com.axzhengxin.platform.common.util.SearchQueryBuilder;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.utils.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.highlight.HighlightField;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * DirectElasticSearchServiceImpl
 *
 * @author jiangnan
 * @date 2021/3/18
 */
@Slf4j
@Service
public class DirectElasticSearchServiceImpl implements IElasticSearchAgentService {

    private static final Map<String, String> RANGE_FILTER_KEY_MAPPER;

    private static final Map<String, String> TERM_FILTER_KEY_MAPPER;

    static {
        Map<String, String> tmpMap1 = MapUtil.<String, String>builder()
                .put("areaName", "OPLOCSZDS")
                .put("entState", "STATE")
                .put("entType", "INDUSTRYPHY")
                .put("entHotType", "INDUSRMHY")
                .build();
        Map<String, String> tmpMap2 = MapUtil.<String, String>builder()
                .put("regcap", "REGCAP")
                .put("estDate", "ESTDATE")
                .build();
        RANGE_FILTER_KEY_MAPPER = Collections.unmodifiableMap(tmpMap2);
        TERM_FILTER_KEY_MAPPER = Collections.unmodifiableMap(tmpMap1);
    }

    @Autowired
    private ObjectMapper mapper;

    @Override
    public ElasticSearchAgentResponse getEntInfoList(ElasticSearchAgentRequest request) {
        // 创建搜索构造器
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 添加过滤条件
        addFilterQuery(boolQueryBuilder, request.getFilters());
        // 添加匹配条件
        addMatchQuery(boolQueryBuilder, request.getSearchKey(), request.getSearchType());
        // 执行搜索
        SearchResponse searchResponse = ElasticSearchUtil.getClient()
                .prepareSearch("sjqdata")
                .setTypes("sjq_jbxx")
                .setQuery(boolQueryBuilder)
                .setSize(request.getSize())
                .setFrom(request.getFrom())
                .setExplain(true)
                /*.addHighlightedField("ENTNAME")
                .addHighlightedField("LEREP")
                .addHighlightedField("OPSCOPE")*/
                .execute().actionGet();
        ElasticSearchAgentResponse response = new ElasticSearchAgentResponse();
        response.setTotal(searchResponse.getHits().getTotalHits());
        List<EntInfo> data = Stream.of(searchResponse.getHits().getHits())
                .map(this::parseHit)
                .filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toList());
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
        SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd");
        data.stream().forEach(entInfo->{
            Date parse = null;
            try {
                parse = simpleDateFormat.parse(entInfo.getEstdate());
                String format = simpleDateFormat1.format(parse);
                entInfo.setEstdate(format);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        });
        response.setData(data);
        return response;
    }

    /**
     * 解析查询匹配到的结果
     *
     * @param hit 匹配到的原始结果
     * @return 解析后的企业信息对象
     */
    private Optional<EntInfo> parseHit(@NotNull SearchHit hit) {
        return Optional.ofNullable(hit.sourceAsString())
                .map(s -> s.replace("null", "\"\""))
                .map(s -> {
                    try {
                        EntInfo entInfo = mapper.readValue(s, EntInfo.class);
                        Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                        fillHighlightFieldValue(highlightFields.get("ENTNAME"), entInfo::setEntname);
                        fillHighlightFieldValue(highlightFields.get("LEREP"), entInfo::setLerep);
                        fillHighlightFieldValue(highlightFields.get("OPSCOPE"), entInfo::setOpscope);
                        return entInfo;
                    } catch (JsonProcessingException e) {
                        e.printStackTrace();
                        return null;
                    }
                });
    }

    /**
     * 写入亮字段值
     *
     * @param field    高亮字段
     * @param consumer 赋值方法引用
     */
    private void fillHighlightFieldValue(HighlightField field, Consumer<String> consumer) {
        Optional.ofNullable(field)
                .map(HighlightField::fragments)
                .filter(arr -> arr.length >= 1)
                .map(arr -> arr[0])
                .map(Text::toString)
                .ifPresent(consumer);
    }

    /**
     * 写入过滤条件到搜索条件
     *
     * @param queryBuilder 搜索构造器
     * @param filters      过滤条件集合
     */
    private void addFilterQuery(@NotNull BoolQueryBuilder queryBuilder,
                                @NotNull List<ElasticSearchAgentRequest.QueryFilter> filters) {
        filters.stream()
                .filter(f -> RANGE_FILTER_KEY_MAPPER.containsKey(f.getFilterKey()))
                .filter(f -> StrUtil.isNotBlank(f.getFilterValue()))
                .filter(f -> !"null".equalsIgnoreCase(f.getFilterValue()))
                .forEach(f -> SearchQueryBuilder.setRangeQueryLevel(queryBuilder,
                        RANGE_FILTER_KEY_MAPPER.get(f.getFilterKey()), f.getFilterValue()));
        filters.stream()
                .filter(f -> TERM_FILTER_KEY_MAPPER.containsKey(f.getFilterKey()))
                .filter(f -> StrUtil.isNotBlank(f.getFilterValue()))
                .filter(f -> !"null".equalsIgnoreCase(f.getFilterValue()))
                .forEach(f -> SearchQueryBuilder.setTermQuery(queryBuilder,
                        TERM_FILTER_KEY_MAPPER.get(f.getFilterKey()), f.getFilterValue()));
    }

    /**
     * 写入搜索关键字到搜索条件
     *
     * @param queryBuilder 搜索构造器
     * @param searchKey    搜索关键字
     * @param searchType   搜索范围
     */
    private void addMatchQuery(@NotNull BoolQueryBuilder queryBuilder,
                               @NotNull String searchKey,
                               @NotNull String searchType) {
        MultiMatchQueryBuilder multiMatchQuery;
        MatchQueryBuilder matcQuery;
        switch (searchType) {
            case "ALL":
                multiMatchQuery = QueryBuilders.multiMatchQuery(searchKey, "ENTNAME", "LEREP", "OPSCOPE");
                queryBuilder.must(multiMatchQuery);
                break;
            case "ENTNAME":
                matcQuery = QueryBuilders.matchQuery("ENTNAME", searchKey);
                queryBuilder.must(matcQuery);
                break;
            case "LEREP":
                matcQuery = QueryBuilders.matchQuery("LEREP", searchKey);
                queryBuilder.must(matcQuery);
                break;
            case "OPSCOPE":
                matcQuery = QueryBuilders.matchQuery("OPSCOPE", searchKey);
                queryBuilder.must(matcQuery);
                break;
            default:
                break;
        }
    }

}
